package com.yunquan.ohana.ui.calendar.event.add

import android.app.Application
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.databinding.ObservableArrayList
import androidx.databinding.ObservableField
import androidx.lifecycle.LifecycleOwner
import com.google.gson.Gson
import com.yunquan.ohana.BR
import com.yunquan.ohana.R
import com.yunquan.ohana.base.binding.command.BindingAction
import com.yunquan.ohana.base.binding.command.BindingCommand
import com.yunquan.ohana.base.binding.command.BindingConsumer
import com.yunquan.ohana.base.bus.event.SingleLiveEvent
import com.yunquan.ohana.base.ui.AppManager
import com.yunquan.ohana.base.ui.BaseViewModel
import com.yunquan.ohana.entity.RoleEntity
import com.yunquan.ohana.entity.ScheduleModel
import com.yunquan.ohana.entity.ScheduleModelVo
import com.yunquan.ohana.http.BaseHttp
import com.yunquan.ohana.http.observer.BaseObserver
import com.yunquan.ohana.http.transformer.CommonTransformer
import com.yunquan.ohana.ui.calendar.event.week.dialog.BottomDeleteDialog
import com.yunquan.ohana.ui.calendar.profiles.add.AddProfilesActivity
import com.yunquan.ohana.utils.DateTimeHelper
import com.yunquan.ohana.utils.StringUtils
import com.yunquan.ohana.utils.ToastUtils
import com.yunquan.ohana.utils.Utils
import io.reactivex.rxjava3.disposables.Disposable
import me.tatarka.bindingcollectionadapter2.ItemBinding
import okhttp3.RequestBody.Companion.toRequestBody
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter

class AddEventViewModel(application: Application) : BaseViewModel(application) {

    val ui = SingleLiveEvent<Int>()

    val title = ObservableField("Add Event")
    val eventTitle = ObservableField<String>()
    val isAllDay = ObservableField(false)
    val startDate = ObservableField<String>()
    val startTime = ObservableField<String>()
    val endDate = ObservableField<String>()
    val endTime = ObservableField<String>()
    val isRepeats = ObservableField(false)
    val everyCount = ObservableField("1")
    val everyTypeIndex = ObservableField(0)
    val repeatsUntil = ObservableField<String>()

    val everyWeekSun = ObservableField(false)
    val everyWeekMon = ObservableField(false)
    val everyWeekTue = ObservableField(false)
    val everyWeekWen = ObservableField(false)
    val everyWeekThu = ObservableField(false)
    val everyWeekFri = ObservableField(false)
    val everyWeekSat = ObservableField(false)

    val isUserType = ObservableField(false)
    val isShowDelete = ObservableField(false)

    val roleItems = ObservableArrayList<AddEventRoleItemViewModel<AddEventViewModel>>()
    val roleItemBinding = ItemBinding.of<AddEventRoleItemViewModel<AddEventViewModel>>(
        BR.addEventRoleItemViewModel,
        R.layout.item_add_edit_task_role,
    )

    var mStartDate: LocalDate? = null
    var mStartTime: LocalTime? = null
    var mEndDate: LocalDate? = null
    var mEndTime: LocalTime? = null
    var mRepeatsUntilDate: LocalDate? = null

    val mDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    val mTimeFormat = DateTimeFormatter.ofPattern("HH:mm")

    var mDeviceId: String? = null

    var mData: ScheduleModel? = null

    val onStartDateClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            DateTimeHelper.showDatePicker(mStartDate) { temp ->
                if (mEndDate != null && temp.isAfter(mEndDate)) {
                    ToastUtils.showShort(R.string.tip_end_less_start)
                    return@showDatePicker
                }
                mStartDate = temp
                startDate.set(mStartDate!!.format(mDateFormat))
                everyWeekSun.set(false)
                everyWeekMon.set(false)
                everyWeekTue.set(false)
                everyWeekWen.set(false)
                everyWeekThu.set(false)
                everyWeekFri.set(false)
                everyWeekSat.set(false)
                when (temp.dayOfWeek.value) {
                    7 -> everyWeekSun.set(true)
                    1 -> everyWeekMon.set(true)
                    2 -> everyWeekTue.set(true)
                    3 -> everyWeekWen.set(true)
                    4 -> everyWeekThu.set(true)
                    5 -> everyWeekFri.set(true)
                    6 -> everyWeekSat.set(true)
                }
            }
        }
    })
    val onStartTimeClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            if (mStartDate == null) {
                ToastUtils.showShort(R.string.error_start_date_tip)
                return
            }
            DateTimeHelper.showTimePicker(mStartTime) { temp ->
                if (mEndDate != null && mEndTime != null && mStartDate!!.isEqual(mEndDate) && !mEndTime!!.isAfter(temp)) {
                    ToastUtils.showShort(R.string.tip_end_less_start)
                    return@showTimePicker
                }
                mStartTime = temp
                startTime.set(mStartTime!!.format(mTimeFormat))
            }
        }
    })
    val onEndDateClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            DateTimeHelper.showDatePicker(mEndDate) { temp ->
                if (mStartDate != null && mStartDate!!.isAfter(temp)) {
                    ToastUtils.showShort(R.string.tip_end_less_start)
                    return@showDatePicker
                }
                mEndDate = temp
                endDate.set(mEndDate!!.format(mDateFormat))
            }
        }
    })
    val onEndTimeClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            if (mEndDate == null) {
                ToastUtils.showShort(R.string.error_end_date_tip)
                return
            }
            DateTimeHelper.showTimePicker(mEndTime) { temp ->
                if (mStartDate != null && mStartTime != null && mStartDate!!.isEqual(mEndDate) && !temp.isAfter(mStartTime)) {
                    ToastUtils.showShort(R.string.tip_end_less_start)
                    return@showTimePicker
                }
                mEndTime = temp
                endTime.set(mEndTime!!.format(mTimeFormat))
            }
        }
    })

    val onAddEveryClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val count = everyCount.get()!!.toInt()
            everyCount.set((count + 1).toString())
        }
    })

    val onReduceEveryClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val count = everyCount.get()!!.toInt()
            if (count > 1) {
                everyCount.set((count - 1).toString())
            } else {
                ToastUtils.showShort(R.string.the_minimum_value_has_been_reached)
            }
        }
    })

    val onEveryGroupClick = BindingCommand(object : BindingConsumer<Int> {
        override fun call(t: Int) {
            everyTypeIndex.set(
                when (t) {
                    R.id.rb_week -> 1
                    R.id.rb_month -> 2
                    R.id.rb_year -> 3
                    else -> 0
                }
            )
        }
    })
    val onRepeatsUntilDateClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            DateTimeHelper.showDatePicker(mRepeatsUntilDate) { temp ->
                mRepeatsUntilDate = temp
                repeatsUntil.set(mRepeatsUntilDate!!.format(mDateFormat))
            }
        }
    })

    val onAddProfilesClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val bundle = Bundle()
            bundle.putString("deviceId", mDeviceId)
            bundle.putBoolean("userType", isUserType.get() ?: false)
            startActivityResult(AddProfilesActivity::class.java, bundle)
        }
    })
    val onSaveClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            putSaveEvent()
        }
    })

    val onDeleteClick = BindingCommand<Any>(object : BindingAction {
        override fun call() {
            val deleteDialog = BottomDeleteDialog(AppManager.appManager.currentActivity()!!)
            deleteDialog.setTitle(Utils.instance.getString(R.string.delete))
            deleteDialog.setTip(Utils.instance.getString(R.string.task_delete_tip))
            deleteDialog.setOnDeleteClick { _ ->
                deleteEvent()
            }
            deleteDialog.show()
        }
    })

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        repeatsUntil.set(Utils.instance.getString(R.string.select_date))
        getProfilesList()
    }

    fun getProfilesList() {
        val map = mutableMapOf<String, Any>()
        map["deviceId"] = mDeviceId!!
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.getDeviceRoleListById(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<List<RoleEntity>>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: List<RoleEntity>?) {
                    super.onSuccess(result)
                    roleItems.clear()
                    result?.forEach {
                        val check = if (mData != null) {
                            val scheduleRole = mData?.scheduleToRoleVos?.firstOrNull { role ->
                                role.roleId == it.id
                            }
                            scheduleRole != null
                        } else {
                            false
                        }
                        roleItems.add(AddEventRoleItemViewModel(this@AddEventViewModel, it, check))
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    private fun putSaveEvent() {
        if (StringUtils.isEmpty(eventTitle.get())) {
            ToastUtils.showShort(R.string.error_task_title_tip)
            return
        }
        val roleList = roleItems.filter { it.checked.get() == true }.map { it.data!! }.toList()
        if (!roleList.isNotEmpty()) {
            ToastUtils.showShort(R.string.error_role_tip)
            return
        }
        if (mStartDate == null) {
            ToastUtils.showShort(R.string.error_start_time_tip)
            return
        }
        if (mEndDate == null) {
            ToastUtils.showShort(R.string.error_end_time_tip)
            return
        }
        if (isAllDay.get() == true) {
            mStartTime = LocalTime.of(0, 0, 0)
            mEndTime = LocalTime.of(23, 59, 59)
        } else {
            if (mStartTime == null) {
                ToastUtils.showShort(R.string.error_start_time_tip)
                return
            }
            if (mEndTime == null) {
                ToastUtils.showShort(R.string.error_end_time_tip)
                return
            }
        }
        val mStartDateTime = LocalDateTime.of(mStartDate, mStartTime)
        val mEndDateTime = LocalDateTime.of(mEndDate, mEndTime)
        if (mStartDateTime.isAfter(mEndDateTime)) {
            ToastUtils.showShort(R.string.tip_end_less_start)
            return
        }
        if (isRepeats.get() == true && !mStartDate!!.isEqual(mEndDate)) {
            ToastUtils.showShort(R.string.tip_repeats_event_cannot_cross_days)
            return
        }
        val mRepeatsUntilDateTime = if (mRepeatsUntilDate != null) {
            LocalDateTime.of(mRepeatsUntilDate, LocalTime.of(0, 0, 0))
        } else {
            null
        }
        val data = ScheduleModelVo()
        data.id = mData?.id
        data.deviceId = mDeviceId
        data.title = eventTitle.get()
        data.startTime = mStartDateTime.atZone(ZoneId.systemDefault()).toEpochSecond()
        data.endTime = mEndDateTime.atZone(ZoneId.systemDefault()).toEpochSecond()
        data.allDay = if (isAllDay.get() == true) 1 else 0
        data.repeatsUntil = mRepeatsUntilDateTime?.atZone(ZoneId.systemDefault())?.toEpochSecond()?.toString()
        if (isRepeats.get() == true) {
            data.repeats = "1"
            data.every = everyCount.get()?.toInt()
            data.recurrence = (everyTypeIndex.get()!! + 1).toString()
            if (everyTypeIndex.get() == 1) {
                val sb = StringBuilder()
                if (everyWeekSun.get() == true) {
                    sb.append("1").append(",")
                }
                if (everyWeekMon.get() == true) {
                    sb.append("2").append(",")
                }
                if (everyWeekTue.get() == true) {
                    sb.append("3").append(",")
                }
                if (everyWeekWen.get() == true) {
                    sb.append("4").append(",")
                }
                if (everyWeekThu.get() == true) {
                    sb.append("5").append(",")
                }
                if (everyWeekFri.get() == true) {
                    sb.append("6").append(",")
                }
                if (everyWeekSat.get() == true) {
                    sb.append("7").append(",")
                }
                if (sb.isNotEmpty()) {
                    data.repetitiveTime = sb.substring(0, sb.length - 1)
                } else {
                    ToastUtils.showShort(R.string.please_select_at_least_one_repetition_time)
                    return
                }
            }
        } else {
            data.repeats = "0"
        }
        data.deviceRoleDtoList = roleList.map {
            val pair = Pair<String, Any?>("roleId", it.id)
            mutableMapOf(pair)
        }.toMutableList()
        if (mData != null) {
            editEvent(data)
        } else {
            createEvent(data)
        }
    }

    private fun createEvent(data: ScheduleModelVo) {
        val body = Gson().toJson(data).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.createSchedule(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<ScheduleModel>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: ScheduleModel?) {
                    super.onSuccess(result)
                    if (result?.id != null) {
                        Handler(Looper.getMainLooper()).postDelayed({
                            ui.value = 0
                        }, 500)
                    } else {
                        ToastUtils.showShort(R.string.failed_to_create_schedule)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    private fun editEvent(data: ScheduleModelVo) {
        val body = Gson().toJson(data).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.updateSchedulesById(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        ui.value = 0
                    } else {
                        ToastUtils.showShort(R.string.edit_schedule_failure)
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }

    private fun deleteEvent() {
        if (mData?.id == null) {
            ToastUtils.showShort(R.string.delete_failed)
            return
        }
        val map = mutableMapOf<String, Any?>()
        map["deviceId"] = mDeviceId
        map["scheduleId"] = listOf(mData?.id)
        val body = Gson().toJson(map).toRequestBody(BaseHttp.MEDIA_TYPE_JSON)
        BaseHttp.api.deleteSchedule(body)
            .compose(CommonTransformer())
            .subscribe(object : BaseObserver<Boolean>() {
                override fun onSubscribe(d: Disposable) {
                    super.onSubscribe(d)
                    showLoading()
                }

                override fun onSuccess(result: Boolean?) {
                    super.onSuccess(result)
                    if (result == true) {
                        ui.value = 1
                    }
                }

                override fun onComplete() {
                    super.onComplete()
                    hideLoading()
                }
            })
    }
}